﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ProgressTracker.Data.Abstract;
using ProgressTracker.Data.Entities;
using ProgressTracker.Domain.Messages;
using ProgressTracker.Domain.Services;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ProgressTracker.Tests.Domain.Services
{
    [TestClass]
    public class TaskServiceTests
    {
        private Mock<IDayRepository> _dayRepo;
        private Mock<ITaskRepository> _taskRepo;
        private Mock<IUserRepository> _userRepo;

        private User _testUser;
        private ProgressTracker.Data.Entities.Task _testTask;
        private ProgressTracker.Data.Entities.Day _testDay;
        private AddTaskRequest _testAddRequest;
        private UpdateTaskRequest _testUpdateRequest;

        [TestInitialize]
        public void Initialize()
        {
            _dayRepo = new Mock<IDayRepository>();
            _taskRepo = new Mock<ITaskRepository>();
            _userRepo = new Mock<IUserRepository>();

            _testUser = new User
            {
                Email = "fake@fake.com",
                Id = 1,
            };

            _testTask = new ProgressTracker.Data.Entities.Task
            {
                Description = "Test Description",
                Id = 1,
                Name = "Test",
                User = _testUser
            };

            _testDay = new Day
            {
                Date = DateTime.Today,
                Id = 2,
                Notes = "Test",
                User = _testUser
            };

            _testAddRequest = new AddTaskRequest
            {
                Description = "testDesc",
                Name = "test",
                UserEmail = _testUser.Email
            };

            _testUpdateRequest = new UpdateTaskRequest
            {
                Description = "New Desc",
                Id = _testTask.Id,
                Name = "New Name"
            };

            _userRepo.Setup(x => x.GetUserByEmail(It.IsAny<string>()))
                .Returns(_testUser);

            ProgressTracker.Data.Entities.Day nullDay = null; //needed so that compiler doesn't complain.
            _dayRepo.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>())).Returns(nullDay);
        }

        [TestMethod]
        public void AddTask_Gets_Correct_User()
        {
            _userRepo.Setup(x => x.GetUserByEmail(_testAddRequest.UserEmail))
                .Returns(_testUser).Verifiable();

            _taskRepo.Setup(x => x.Add(It.IsAny<ProgressTracker.Data.Entities.Task>()));

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.AddTask(_testAddRequest);

            _userRepo.Verify();
        }

        [TestMethod]
        public void AddTask_Adds_Task_To_Repo()
        {
            _taskRepo.Setup(x => x.Add(It.Is<ProgressTracker.Data.Entities.Task>(
                t => t.Description == _testAddRequest.Description
                && t.Name == _testAddRequest.Name
                && t.User.Email == _testAddRequest.UserEmail))).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.AddTask(_testAddRequest);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void AddTask_Adds_Task_To_Current_Day()
        {
            _dayRepo.Setup(x => x.GetDay(DateTime.Today, _testAddRequest.UserEmail))
                .Returns(_testDay).Verifiable();
            _dayRepo.Setup(x => x.AddTaskToDay(_testDay, It.Is<Task>(
                t => t.Name == _testAddRequest.Name && t.Description == _testAddRequest.Description))).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.AddTask(_testAddRequest);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void GetCurrentTasks_Gets_Current_Tasks_From_Repo()
        {
            _taskRepo.Setup(x => x.GetCurrentTasks(_testUser.Id))
                .Returns(new List<ProgressTracker.Data.Entities.Task>())
                .Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            var tasks = taskService.GetCurrentTasks(_testUser.Email);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void GetCurrentTasks_Returns_Tasks()
        {
            _taskRepo.Setup(x => x.GetCurrentTasks(_testUser.Id))
                .Returns(new List<ProgressTracker.Data.Entities.Task>()
                {
                    new ProgressTracker.Data.Entities.Task
                    {
                        Id = 1,
                    },
                    new ProgressTracker.Data.Entities.Task
                    {
                        Id = 2,
                    }
                });

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            var tasks = taskService.GetCurrentTasks(_testUser.Email);

            Assert.IsTrue(tasks.Where(x => x.Id == 1).Count() == 1);
            Assert.IsTrue(tasks.Where(x => x.Id == 2).Count() == 1);
        }

        [TestMethod]
        public void GetTask_Retrieves_Task_From_Repo()
        {
            _taskRepo.Setup(x => x.GetTaskById(_testTask.Id))
                .Returns(_testTask).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            var task = taskService.GetTask(_testTask.Id);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void UpdateTask_Retrieves_Correct_Task_To_Update()
        {
            _taskRepo.Setup(x => x.GetTaskById(_testUpdateRequest.Id))
                .Returns(_testTask).Verifiable();

            _taskRepo.Setup(x => x.Update(It.IsAny<ProgressTracker.Data.Entities.Task>()));

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.UpdateTask(_testUpdateRequest);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void UpdateTask_Updates_Task()
        {
            _taskRepo.Setup(x => x.GetTaskById(It.IsAny<int>()))
                .Returns(_testTask);

            _taskRepo.Setup(x => x.Update(It.Is<ProgressTracker.Data.Entities.Task>(
                t => t.Description == _testUpdateRequest.Description
                && t.Id == _testUpdateRequest.Id
                && t.Name == _testUpdateRequest.Name))).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.UpdateTask(_testUpdateRequest);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void DeleteTask_Retrieves_Correct_Task_To_Delete()
        {
            _taskRepo.Setup(x => x.GetTaskById(_testUpdateRequest.Id))
                .Returns(_testTask).Verifiable();

            _taskRepo.Setup(x => x.Delete(It.IsAny<ProgressTracker.Data.Entities.Task>()));

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.DeleteTask(_testTask.Id);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void DeleteTask_Calls_Repo_Delete()
        {
            _taskRepo.Setup(x => x.GetTaskById(It.IsAny<int>()))
                .Returns(_testTask);

            _taskRepo.Setup(x => x.Delete(_testTask)).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.DeleteTask(_testTask.Id);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void ArchiveTask_Retrieves_Correct_Task_To_Archive()
        {
            _taskRepo.Setup(x => x.GetTaskById(_testUpdateRequest.Id))
                .Returns(_testTask).Verifiable();

            _taskRepo.Setup(x => x.Update(It.IsAny<ProgressTracker.Data.Entities.Task>()));

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.ArchiveTask(_testTask.Id);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void ArchiveTasks_Sets_Archived_To_True()
        {
            _taskRepo.Setup(x => x.GetTaskById(_testUpdateRequest.Id))
                .Returns(_testTask).Verifiable();

            _taskRepo.Setup(x => x.Update(It.Is<Task>(
                t => t.Id == _testTask.Id
                && t.Archived == true))).Verifiable();

            var taskService = new TaskService(_dayRepo.Object, _taskRepo.Object, _userRepo.Object);
            taskService.ArchiveTask(_testTask.Id);

            _taskRepo.Verify();
        }
    }
}
